﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using GeoUtils;

namespace GeocoderRankerFeatureGenerator
{
    public class GeocoderRankerFeatureGenerator
    {
        static public string Generate(List<Token> tokenList, GeoEntityChain geoEntityChain, ViewPort viewPort)
        {
            StringBuilder sbFeatureSet = new StringBuilder();
            string strFeatureValue = "";
            int featureId = 0;

            //1
            featureId++;
            strFeatureValue = Is_Geo_Item_Leaf_Rewritten_Road_TopSecondLevel(geoEntityChain);
            sbFeatureSet.Append(featureId.ToString() + ":");
            sbFeatureSet.Append(strFeatureValue);
            sbFeatureSet.Append(" ");

            //2
            featureId++;
            strFeatureValue = Covered_Query_Token_Ratio(tokenList, geoEntityChain);
            sbFeatureSet.Append(featureId.ToString() + ":");
            sbFeatureSet.Append(strFeatureValue);
            sbFeatureSet.Append(" ");

            //3
            featureId++;
            strFeatureValue = Geo_Item_Block_Number_In_Query(tokenList, geoEntityChain);
            sbFeatureSet.Append(featureId.ToString() + ":");
            sbFeatureSet.Append(strFeatureValue);
            sbFeatureSet.Append(" ");

            //4
            featureId++;
            strFeatureValue = GeoChain_Count(geoEntityChain);
            sbFeatureSet.Append(featureId.ToString() + ":");
            sbFeatureSet.Append(strFeatureValue);
            sbFeatureSet.Append(" ");

            //5
            featureId++;
            strFeatureValue = Entire_GeoChain_Covered_Query_Contigous_Token(tokenList, geoEntityChain);
            sbFeatureSet.Append(featureId.ToString() + ":");
            sbFeatureSet.Append(strFeatureValue);
            sbFeatureSet.Append(" ");

            //6
            featureId++;
            strFeatureValue = Is_From_To_In_Query(tokenList, geoEntityChain);
            sbFeatureSet.Append(featureId.ToString() + ":");
            sbFeatureSet.Append(strFeatureValue);
            sbFeatureSet.Append(" ");

            //7
            featureId++;
            strFeatureValue = Query_Token_Geo_Chain_Order(tokenList, geoEntityChain);
            sbFeatureSet.Append(featureId.ToString() + ":");
            sbFeatureSet.Append(strFeatureValue);
            sbFeatureSet.Append(" ");

            //8
            featureId++;
            strFeatureValue = GeoChain_Root_Level(geoEntityChain);
            sbFeatureSet.Append(featureId.ToString() + ":");
            sbFeatureSet.Append(strFeatureValue);
            sbFeatureSet.Append(" ");

            //9
            featureId++;
            strFeatureValue = GeoChain_Leaf_Level(geoEntityChain);
            sbFeatureSet.Append(featureId.ToString() + ":");
            sbFeatureSet.Append(strFeatureValue);
            sbFeatureSet.Append(" ");

            //10
            featureId++;
            strFeatureValue = Average_Geo_Chain_Level(geoEntityChain);
            sbFeatureSet.Append(featureId.ToString() + ":");
            sbFeatureSet.Append(strFeatureValue);
            sbFeatureSet.Append(" ");

            //11
            featureId++;
            strFeatureValue = GeoChain_Has_Rewritten(geoEntityChain);
            sbFeatureSet.Append(featureId.ToString() + ":");
            sbFeatureSet.Append(strFeatureValue);
            sbFeatureSet.Append(" ");

            //12
            featureId++;
            strFeatureValue = GeoChain_Leaf_Rewritten(geoEntityChain);
            sbFeatureSet.Append(featureId.ToString() + ":");
            sbFeatureSet.Append(strFeatureValue);
            sbFeatureSet.Append(" ");

            //13
            featureId++;
            strFeatureValue = GeoChain_Rewritten_Number(geoEntityChain);
            sbFeatureSet.Append(featureId.ToString() + ":");
            sbFeatureSet.Append(strFeatureValue);
            sbFeatureSet.Append(" ");

            //14
            featureId++;
            strFeatureValue = GeoChain_Rewritten_Ratio(geoEntityChain);
            sbFeatureSet.Append(featureId.ToString() + ":");
            sbFeatureSet.Append(strFeatureValue);
            sbFeatureSet.Append(" ");

            //15
            featureId++;
            strFeatureValue = GeoEntityChain_In_Viewport_Number(viewPort, geoEntityChain);
            sbFeatureSet.Append(featureId.ToString() + ":");
            sbFeatureSet.Append(strFeatureValue);
            sbFeatureSet.Append(" ");

            //16
            featureId++;
            strFeatureValue = GeoChain_Is_Leaf_In_Viewport(viewPort, geoEntityChain);
            sbFeatureSet.Append(featureId.ToString() + ":");
            sbFeatureSet.Append(strFeatureValue);
            sbFeatureSet.Append(" ");

            //17
            featureId++;
            strFeatureValue = GeoChain_Is_Root_In_Viewport(viewPort, geoEntityChain);
            sbFeatureSet.Append(featureId.ToString() + ":");
            sbFeatureSet.Append(strFeatureValue);
            sbFeatureSet.Append(" ");

            //18
            featureId++;
            strFeatureValue = GeoEntity_In_Leaf_Distance_To_ViewPort(viewPort, geoEntityChain);
            sbFeatureSet.Append(featureId.ToString() + ":");
            sbFeatureSet.Append(strFeatureValue);
            sbFeatureSet.Append(" ");

            //19
            featureId++;
            strFeatureValue = Is_Geo_Item_Root_At_The_Head_Of_Query(geoEntityChain);
            sbFeatureSet.Append(featureId.ToString() + ":");
            sbFeatureSet.Append(strFeatureValue);
            sbFeatureSet.Append(" ");

            //20
            featureId++;
            strFeatureValue = Is_Geo_Item_Root_At_The_Tail_Of_Query(tokenList, geoEntityChain);
            sbFeatureSet.Append(featureId.ToString() + ":");
            sbFeatureSet.Append(strFeatureValue);
            sbFeatureSet.Append(" ");

            //21
            featureId++;
            strFeatureValue = Is_Geo_Entity_Leaf_At_The_Head_Of_Query(geoEntityChain);
            sbFeatureSet.Append(featureId.ToString() + ":");
            sbFeatureSet.Append(strFeatureValue);
            sbFeatureSet.Append(" ");

            //22
            featureId++;
            strFeatureValue = Is_Geo_Entity_Leaf_At_The_Tail_Of_Query(tokenList, geoEntityChain);
            sbFeatureSet.Append(featureId.ToString() + ":");
            sbFeatureSet.Append(strFeatureValue);
            sbFeatureSet.Append(" ");

            //23
            featureId++;
            strFeatureValue = Is_Geo_Entity_Leaf_Part_Of_ORG(tokenList, geoEntityChain);
            sbFeatureSet.Append(featureId.ToString() + ":");
            sbFeatureSet.Append(strFeatureValue);
            sbFeatureSet.Append(" ");

            //24
            featureId++;
            strFeatureValue = Is_GeoEntity_In_Leaf_In_ViewPort(viewPort, geoEntityChain);
            sbFeatureSet.Append(featureId.ToString() + ":");
            sbFeatureSet.Append(strFeatureValue);
            sbFeatureSet.Append(" ");

            return sbFeatureSet.ToString();
        }

        //Judge whether it is trans query
        static string Is_From_To_In_Query(List<Token> tokenList, GeoEntityChain geoItemChain)
        {
            for (int i = 0; i < geoItemChain.Size() - 1; i++)
            {
                int idx1 = tokenList.IndexOf(geoItemChain.geoTokenList[i]);
                int idx2 = tokenList.IndexOf(geoItemChain.geoTokenList[i + 1]);

                for (int j = idx1 + 1; j < idx2; j++)
                {
                    if (tokenList[j].strTerm == "到" || tokenList[j].strTerm == "去" || tokenList[j].strTerm == "前往")
                    {
                        return "1";
                    }
                }
            }

            return "0";
        }


        //Check whether geo-entity as chain leaf is a part of organization name
        static string Is_Geo_Entity_Leaf_Part_Of_ORG(List<Token> tokenList, GeoEntityChain geoEntityChain)
        {
            //KWD_ORG_MOD
            Token token = geoEntityChain.geoTokenList[geoEntityChain.Size() - 1];
            int idx = tokenList.IndexOf(token);

            if (idx == tokenList.Count - 1)
            {
                return "0";
            }

            if (tokenList[idx + 1].strTagList.Contains("KWD_ORG_MOD") == true)
            {
                return "1";
            }

            return "0";
        }


        //Whether the root geo-item is at the head of the query
        static string Is_Geo_Item_Root_At_The_Head_Of_Query(GeoEntityChain geoItemChain)
        {
            if (geoItemChain.geoTokenList[0].iOffset == 0)
            {
                return "1";
            }

            return "0";
        }


        //Check whether geo-entity which is the root of the chain is at the end of query
        static string Is_Geo_Item_Root_At_The_Tail_Of_Query(List<Token> tokenList, GeoEntityChain geoItemChain)
        {
            if (geoItemChain.geoTokenList[0].iOffset == tokenList.Count - 1)
            {
                return "1";
            }

            return "0";
        }

        //Check whether the leaf in one geo-item chain is rewritten, has labaled as ROAD tag
        //And the token associated with this item should has other geo-item with Admin1 or Admin2 level
        static string Is_Geo_Item_Leaf_Rewritten_Road_TopSecondLevel(GeoEntityChain geoItemChain)
        {
            if (geoItemChain.geoEntityList[geoItemChain.Size() - 1].bRewritten == true)
            {
                if (geoItemChain.geoTokenList[geoItemChain.Size() - 1].strTagList.Contains("DICT_LOC_ROD") == true &&
                    geoItemChain.geoTokenList[geoItemChain.Size() - 1].candGeoEntityList != null)
                {
                    foreach (GeoEntity item in geoItemChain.geoTokenList[geoItemChain.Size() - 1].candGeoEntityList)
                    {
                        if (item.iLevel <= 2)
                        {
                            return "1";
                        }
                    }
                }
            }

            return "0";
        }

        static string Is_Geo_Entity_Leaf_At_The_Head_Of_Query(GeoEntityChain geoEntityChain)
        {
            if (geoEntityChain.geoTokenList[geoEntityChain.Size() - 1].iOffset == 0)
            {
                return "1";
            }

            return "0";
        }

        static string Is_Geo_Entity_Leaf_At_The_Tail_Of_Query(List<Token> tokenList, GeoEntityChain geoEntityChain)
        {
            if (geoEntityChain.geoTokenList[geoEntityChain.Size() - 1].iOffset == tokenList.Count - 1)
            {
                return "1";
            }

            return "0";
        }


        //The number of geo-item block in query
        static string Geo_Item_Block_Number_In_Query(List<Token> tokenList, GeoEntityChain geoItemChain)
        {
            bool[] abOffset;
            abOffset = new bool[tokenList.Count];
            for (int i = 0; i < abOffset.Length; i++)
            {
                abOffset[i] = false;
            }

            foreach (Token item in geoItemChain.geoTokenList)
            {
                abOffset[item.iOffset] = true;
            }

            int cnt = 0;
            for (int i = 0; i < abOffset.Length - 1; i++)
            {
                if (abOffset[i] != abOffset[i + 1])
                {
                    cnt++;
                }
            }

            return ((cnt / 2) + 1).ToString();
        }



        //Whether an entire geo-chain covers a part of query with contigous token
        static string Entire_GeoChain_Covered_Query_Contigous_Token(List<Token> tokenList, GeoEntityChain geoItemChain)
        {

            for (int i = 0; i <= tokenList.Count - geoItemChain.Size(); i++)
            {
                int iFoundCnt = 0;
                for (int j = i; j < i + geoItemChain.Size(); j++)
                {
                    bool bFoundTokenInGeoChain = false;
                    foreach (GeoEntity item in geoItemChain.geoEntityList)
                    {
                        if (tokenList[j].candGeoEntityList != null &&
                            tokenList[j].candGeoEntityList.Contains(item) == true)
                        {
                            bFoundTokenInGeoChain = true;
                            break;
                        }
                    }

                    if (bFoundTokenInGeoChain == false)
                    {
                        break;
                    }

                    iFoundCnt++;
                }

                if (iFoundCnt == geoItemChain.Size())
                {
                    return "1";
                }
            }

            return "0";
        }

        //The average level in a geo-item chain
        static string Average_Geo_Chain_Level(GeoEntityChain geoEntityChain)
        {
            int total_level = 0;
            foreach (GeoEntity item in geoEntityChain.geoEntityList)
            {
                total_level += item.iLevel;
            }

            double r = (double)total_level / (double)geoEntityChain.Size();

            return r.ToString();

        }


        //Check whether the query token order is the same as geo-item order in a chain
        static string Query_Token_Geo_Chain_Order(List<Token> tokenList, GeoEntityChain geoItemChain)
        {
            int currPos = 0;
            int invCnt = 0;
            foreach (GeoEntity item in geoItemChain.geoEntityList)
            {
                int pos = currPos;
                for (int i = currPos; i < tokenList.Count; i++)
                {
                    if (tokenList[i].candGeoEntityList != null &&
                        tokenList[i].candGeoEntityList.Contains(item) == true)
                    {
                        pos = i;
                        break;
                    }
                }

                if (pos <= currPos)
                {
                    invCnt++;
                }
                else
                {
                    currPos = pos;
                }
            }

            return invCnt.ToString();
        }


        //The coverage rate of geo-item chains on query tokens
        static string Covered_Query_Token_Ratio(List<Token> tokenList, GeoEntityChain geoItemChain)
        {
            double ratio = (double)geoItemChain.Size() / (double)tokenList.Count;
            return ratio.ToString();
        }


        static string GeoChain_Count(GeoEntityChain geoItemChain)
        {
            return geoItemChain.Size().ToString();
        }

        //Get the level of root item in a geo-chain
        static string GeoChain_Root_Level(GeoEntityChain geoItemChain)
        {
            return geoItemChain.geoEntityList[0].iLevel.ToString();
        }

        //Get the level of leaf item in a geo-chain
        static string GeoChain_Leaf_Level(GeoEntityChain geoItemChain)
        {
            return geoItemChain.geoEntityList[geoItemChain.Size() - 1].iLevel.ToString();
        }

        static string GeoChain_Has_Rewritten(GeoEntityChain geoItemChain)
        {
            int iHasRewritten = 0;
            foreach (GeoEntity item in geoItemChain.geoEntityList)
            {
                if (item.bRewritten == true)
                {
                    iHasRewritten = 1;
                    return iHasRewritten.ToString();
                }
            }

            return iHasRewritten.ToString();
        }

        static string GeoChain_Leaf_Rewritten(GeoEntityChain geoItemChain)
        {
            if (geoItemChain.geoEntityList[geoItemChain.Size() - 1].bRewritten == true)
            {
                return "1";
            }
            return "0";
        }

        static string GeoChain_Rewritten_Number(GeoEntityChain geoItemChain)
        {
            int cnt = 0;
            foreach (GeoEntity item in geoItemChain.geoEntityList)
            {
                if (item.bRewritten == true)
                {
                    cnt++;
                }
            }
            return cnt.ToString();
        }

        static string GeoChain_Rewritten_Ratio(GeoEntityChain geoItemChain)
        {
            int cnt = 0;
            foreach (GeoEntity item in geoItemChain.geoEntityList)
            {
                if (item.bRewritten == true)
                {
                    cnt++;
                }
            }

            double ratio = (double)cnt / (double)geoItemChain.Size();
            return ratio.ToString();
        }


        static string GeoEntityChain_In_Viewport_Number(ViewPort viewport,GeoEntityChain geoEntityChain)
        {
            if (viewport == null)
            {
                return "0";
            }

            int cnt = 0;
            foreach (GeoEntity item in geoEntityChain.geoEntityList)
            {
                if (viewport.IsVisible(item.LatLong) == true)
                {
                    cnt++;
                }
            }

            return cnt.ToString();
        }

        static string GeoChain_Is_Root_In_Viewport(ViewPort viewport, GeoEntityChain geoItemChain)
        {
            if (viewport == null)
            {
                return "0";
            }

            if (viewport.IsVisible(geoItemChain.geoEntityList[0].LatLong) == true)
            {
                return "1";
            }

            return "0";
        }

        static string GeoChain_Is_Leaf_In_Viewport(ViewPort viewport, GeoEntityChain geoItemChain)
        {
            if (viewport == null)
            {
                return "0";
            }

            if (viewport.IsVisible(geoItemChain.geoEntityList[geoItemChain.Size() - 1].LatLong) == true)
            {
                return "1";
            }

            return "0";
        }

        static string GeoEntity_In_Leaf_Distance_To_ViewPort(ViewPort viewport, GeoEntityChain geoItemChain)
        {
            if (viewport == null)
            {
                return double.MaxValue.ToString();
            }

            double fDistance = viewport.Distance(geoItemChain.geoEntityList[geoItemChain.Size() - 1].LatLong) / 10000.0;
            return fDistance.ToString();
        }

        static string Is_GeoEntity_In_Leaf_In_ViewPort(ViewPort viewport, GeoEntityChain geoItemChain)
        {
            if (viewport == null)
            {
                return "0";
            }

            bool bRet = viewport.IsVisible(geoItemChain.geoEntityList[geoItemChain.Size() - 1].LatLong);
            if (bRet == true)
            {
                return "1";
            }
            else
            {
                return "0";
            }
        }

    }
}
